package com.peas.platform.module.icims.base.approve.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.dto.BaseDto;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.common.core.exception.BaseException;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.domain.ApproveOrderDomain;
import com.peas.platform.module.icims.base.approve.dto.*;
import com.peas.platform.module.icims.base.approve.enums.*;
import com.peas.platform.module.icims.base.approve.service.*;
import com.peas.platform.module.icims.base.auth.dto.UserDto;
import com.peas.platform.module.icims.base.auth.dto.UserRoleDto;
import com.peas.platform.module.icims.base.auth.service.UserRoleService;
import com.peas.platform.module.icims.base.auth.service.UserService;
import com.peas.platform.module.icims.base.sys.enums.CommonYesNo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ApproveOrderServiceImpl extends BaseServiceImpl<ApproveOrderDto> implements ApproveOrderService {

    @Resource
    private ApproveOrderDomain approveOrderDomain;
    @Resource
    private ApplicationContext ac;
    @Resource
    private ApproveNodeService approveNodeService;
    @Resource
    private ApproveOrderNodeService approveOrderNodeService;
    @Resource
    private ApproveOrderOperService approveOrderOperService;
    @Resource
    private ApproveOrderTobeService approveOrderTobeService;
    @Resource
    private UserService userService;
    @Resource
    private UserRoleService userRoleService;

    @Override
    public BaseDomain<ApproveOrderDto, ? extends BasePo> getDomain() {
        return approveOrderDomain;
    }

    private ApproveOrderNodeDto getCurrApproveOrderNodeDto(ApproveOrderDto aod) {
        List<ApproveOrderNodeDto> aonds = approveOrderNodeService.listByApproveOrderId(aod.getId());
        if (CollUtil.isEmpty(aonds)) {
            return null;
        }
        ApproveOrderNodeDto aond = aonds.get(0);
        Assert.notNull(aond, "找不到审批节点");
        String err = "审批节点" + aond.getApproveNodeNo() + "[" + aond.getApproveNodeName() + "]";
        ApproveNodeSource approveNodeSource = BaseEnum.key2Enum(ApproveNodeSource.class, aond.getApproveNodeSource());
        Assert.notNull(approveNodeSource, err + "，配置[节点来源]不正确");
        ApproveNodeType approveNodeType = BaseEnum.key2Enum(ApproveNodeType.class, aond.getApproveNodeType());
        Assert.notNull(approveNodeType, err + "，配置[节点类型]不正确");
        CommonYesNo isCountersign = BaseEnum.key2Enum(CommonYesNo.class, aond.getIsCountersign());
        Assert.notNull(isCountersign, err + "，配置[是否会签]不正确");
        log.warn("aond:" + aond);
        return aond;
    }

    private boolean pushApproveOrder(ApproveOrderDto aod) {
        String empty = "-1";
        ApproveOrderNodeDto aond = this.getCurrApproveOrderNodeDto(aod);
        if (aond == null) {
            aod.setState(ApproveOrderState.pass.getKey());
            aod.setCurrentApproveOrgId(empty);
            aod.setCurrentApproveRoleId(empty);
            aod.setCurrentApproveUserId(empty);
            aod.setCurrentNo(-1);
            aod.setCurrentName(empty);
            super.updateById(aod);
            // 待阅
            approveOrderTobeService.createApproveOrderTobe(aod.getId(), ApproveOrderTobeType.toview, "审批完成", "您的审批单(" + aod.getApproveOrderName() + ")审批已经完成。", aod.getSubmitUserId(), ApproveOrderTobeState.doing);
            return true;
        }
        ApproveNodeType type = BaseEnum.key2Enum(ApproveNodeType.class, aond.getApproveNodeType());
        Assert.notNull(type, "节点类型不能为空");
        String str = "审批节点" + aond.getApproveNodeNo() + "[" + aond.getApproveNodeName() + "]";
        if (!Objects.equals(aod.getCurrentNo(), aond.getApproveNodeNo())) {
            switch (type) {
                case org: {
                    aod.setCurrentApproveOrgId(aond.getApproveNodeOrgId());
                    aod.setCurrentApproveRoleId(empty);
                    aod.setCurrentApproveUserId(empty);
                    //待办
                    List<Long> orgIds = StrUtil.split(aond.getApproveNodeOrgId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                    List<UserDto> userList = userService.listByField("organizationId", orgIds);
                    Assert.notEmpty(userList, str + "，组织架构下没有用户");
                    userList.forEach(user -> approveOrderTobeService.createApproveOrderTobe(aod.getId(), ApproveOrderTobeType.todo, "审批处理", "您有新的单据(" + aod.getApproveOrderName() + ")待审批，请尽快处理。", user.getId(), ApproveOrderTobeState.doing));
                    break;
                }
                case role: {
                    aod.setCurrentApproveOrgId(empty);
                    aod.setCurrentApproveRoleId(aond.getApproveNodeRoleId());
                    aod.setCurrentApproveUserId(empty);
                    //待办
                    List<Long> roleIds = StrUtil.split(aond.getApproveNodeRoleId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                    List<UserRoleDto> userRoleList = userRoleService.listByField("roleId", roleIds);
                    Assert.notEmpty(userRoleList, str + "，角色下没有用户");
                    List<Long> ids = new ArrayList<>();
                    userRoleList.forEach(urd -> ids.add(urd.getUserId()));
                    List<UserDto> userList = userService.listByIds(ids);
                    userList.forEach(user -> approveOrderTobeService.createApproveOrderTobe(aod.getId(), ApproveOrderTobeType.todo, "审批处理", "您有新的单据(" + aod.getApproveOrderName() + ")待审批，请尽快处理。", user.getId(), ApproveOrderTobeState.doing));
                    break;
                }
                case user: {
                    aod.setCurrentApproveOrgId(empty);
                    aod.setCurrentApproveRoleId(empty);
                    aod.setCurrentApproveUserId(aond.getApproveNodeUserId());
                    //待办
                    List<Long> userIds = StrUtil.split(aod.getCurrentApproveUserId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                    Assert.notNull(userIds, "用户ID为空");
                    userIds.forEach(userId -> approveOrderTobeService.createApproveOrderTobe(aod.getId(), ApproveOrderTobeType.todo, "审批处理", "您有新的单据(" + aod.getApproveOrderName() + ")待审批，请尽快处理。", userId, ApproveOrderTobeState.doing));
                    break;
                }
                default:
                    throw new BaseException("审批节点配置不正确");
            }
        }
        aod.setState(ApproveOrderState.approving.getKey());
        aod.setCurrentNo(aond.getApproveNodeNo());
        aod.setCurrentName(aond.getApproveNodeName());
        super.updateById(aod);
        return false;
    }

    @SneakyThrows
    @Transactional
    @Override
    public void approve(Long approveOrderId, String approveResult, String approveOpinion) {
        final Long currentUserId = Long.valueOf(RequestContextUtil.getCurrentUserId(true));
        Assert.notNull(approveOrderId, "参数错误");
        Assert.notBlank(approveResult, "请输入审批结果");
        ApproveResult result = BaseEnum.key2Enum(ApproveResult.class, approveResult);
        Assert.notNull(result, "审批结果错误");
        if (result == ApproveResult.reject) {
            Assert.notBlank(approveOpinion, "驳回时请输入审批意见");
        }
        ApproveOrderDto aod = this.getById(approveOrderId);
        ApproveOrderNodeDto aond = this.getCurrApproveOrderNodeDto(aod);
        assert aond != null;
        ApproveNodeSource approveNodeSource = BaseEnum.key2Enum(ApproveNodeSource.class, aond.getApproveNodeSource());
        ApproveNodeType approveNodeType = BaseEnum.key2Enum(ApproveNodeType.class, aond.getApproveNodeType());
        CommonYesNo isCountersign = BaseEnum.key2Enum(CommonYesNo.class, aond.getIsCountersign());
        String err = "审批节点" + aond.getApproveNodeNo() + "[" + aond.getApproveNodeName() + "]";
        Assert.notNull(approveNodeSource, err + "，节点来源取值不正确");
        Assert.notNull(approveNodeType, err + "，节点类型取值不正确");
        Assert.notNull(isCountersign, err + "，是否会签取值不正确");
        // 校验
        switch (approveNodeType) {
            case org: {
                List<Long> organizationIds = StrUtil.split(aond.getApproveNodeOrgId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                UserDto currentUser = userService.getById(currentUserId);
                Assert.isTrue(organizationIds.contains(currentUser.getOrganizationId()), "您不在该审批单中待办的组织架构内，无权操作");
                break;
            }
            case role: {
                List<Long> roleIds = StrUtil.split(aond.getApproveNodeRoleId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                List<UserRoleDto> userRoleList = userRoleService.listByField("roleId", roleIds);
                Assert.notEmpty(userRoleList, err + "，角色下没有用户");
                UserRoleDto urd = userRoleList.stream().filter(userRoleDto -> NumberUtil.equals(currentUserId, userRoleDto.getUserId())).findFirst().orElse(null);
                Assert.notNull(urd, "您不在该审批单中待办的角色内，无权操作");
                break;
            }
            case user: {
                List<Long> userIds = StrUtil.split(aond.getApproveNodeUserId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
                Assert.isTrue(userIds.contains(currentUserId), "您不是当前审批人，无权操作");
                break;
            }
        }
        // 是会签
        boolean delOrderNode = true;
        if (CommonYesNo.yes == isCountersign) {
            List<Long> userIds = StrUtil.split(aond.getApproveNodeUserId(), ",").stream().map(Long::valueOf).collect(Collectors.toList());
            Assert.isTrue(userIds.contains(currentUserId), "您不在当前审批会签列表中，无权操作");
            userIds.remove(currentUserId);
            if (CollUtil.isNotEmpty(userIds)) {
                // 会签没批完
                delOrderNode = false;
                aond.setApproveNodeUserId(CollUtil.join(userIds, ","));
                approveOrderNodeService.updateById(aond);
            }
        }
        if (delOrderNode) {
            approveOrderNodeService.removeById(aond.getId());
        }
        // 修改审批待办。非会签修改所有待办、会签修改当前用户的待办
        ApproveOrderTobeDto c = new ApproveOrderTobeDto();
        c.setApproveOrderId(approveOrderId);
        c.setState(ApproveOrderTobeState.doing.getKey());
        if (CommonYesNo.yes == isCountersign) {
            c.setReceiveUserId(currentUserId);
        }
        List<ApproveOrderTobeDto> list = approveOrderTobeService.list(c);
        CollUtil.forEach(list, (CollUtil.Consumer<ApproveOrderTobeDto>) (aotd, index) -> {
            aotd.setState(ApproveOrderTobeState.cancel.getKey());
            if (currentUserId.equals(aotd.getReceiveUserId())) {
                aotd.setState(ApproveOrderTobeState.done.getKey());
                aotd.setReadTime(DateTime.now());
                aotd.setProcessTime(DateTime.now());
            }
            approveOrderTobeService.updateById(aotd);
        });
        aod.setApproveResult(result.getKey());
        aod.setApproveOpinion(approveOpinion);
        // 通过推送下一个节点，驳回创建待阅
        boolean isFinalPass = false;
        switch (result) {
            case pass:
                isFinalPass = this.pushApproveOrder(aod);
                break;
            case reject:
                aod.setState(ApproveOrderState.reject.getKey());
                approveOrderTobeService.createApproveOrderTobe(aod.getId(), ApproveOrderTobeType.todo, "驳回处理", "您的审批单(" + aod.getApproveOrderName() + ")被驳回了，请尽快修改处理吧。", aod.getSubmitUserId(), ApproveOrderTobeState.doing);
                break;
            default:
                throw new BaseException("审批结果错误");
        }
        this.updateById(aod);
        // 保存审批操作
        ApproveOrderBusinessType approveOrderBusinessType = BaseEnum.key2Enum(ApproveOrderBusinessType.class, aod.getBusinessType());
        approveOrderOperService.saveApproveOrderOper(ApproveOrderOperType.approve, aod, aond.getApproveNodeNo(), aond.getApproveNodeName(), result.getKey(), approveOpinion, approveOrderBusinessType, aod.getBusinessId());
        //回调
        Class<?> clazz = Class.forName(aod.getBusinessClass());
        ApproveCallback call = (ApproveCallback) ac.getBean(clazz);
        call.callbackAfterApprove(aod.getBusinessId(), ApproveResult.pass == result, isFinalPass);
    }

    @Transactional
    @Override
    public void createApproveOrder(ApproveDto ad, List<ApproveOrderNodeDto> aonds, ApproveOrderBusinessType businessType, Long businessId, String businessDesc, String businessUrl, Class<? extends ApproveCallback> clazz) {
        final Long currentUserId = Long.valueOf(RequestContextUtil.getCurrentUserId(true));
        ApproveOrderDto aod = new ApproveOrderDto();
        aod.setApproveId(ad.getId());
        aod.setApproveOrderCode(null);
        String desc = StrUtil.isBlank(businessDesc) ? String.valueOf(businessId) : businessDesc;
        aod.setApproveOrderName(businessType.getValue() + "[" + desc + "]审批单");
        aod.setState(ApproveOrderState.approving.getKey());
        aod.setSubmitUserId(currentUserId);
        aod.setCurrentNo(0);
        aod.setCurrentName(null);
        aod.setApproveResult(null);
        aod.setApproveOpinion(null);
        aod.setBusinessType(businessType.getKey());
        aod.setBusinessId(businessId);
        aod.setBusinessUrl(businessUrl);
        aod.setBusinessClass(clazz.getName());
        this.save(aod);
        // 审批节点
        if (CollUtil.isEmpty(aonds)) {
            List<ApproveNodeDto> nodes = approveNodeService.listByApproveId(aod.getApproveId());
            Assert.notEmpty(nodes, "匹配的审批模版没有审批节点");
            aonds = nodes.stream().map((Function<ApproveNodeDto, ApproveOrderNodeDto>) approveNodeDto -> {
                ApproveOrderNodeDto aond = BeanUtil.copyProperties(approveNodeDto, ApproveOrderNodeDto.class);
                aond.setApproveNodeSource(ApproveNodeSource.template.getKey());
                return null;
            }).collect(Collectors.toList());
            Assert.notEmpty(aonds, "没有审批节点");
        }
        aonds.forEach(aond -> {
            Assert.notBlank(aond.getApproveNodeSource(), "审批节点来源为空");
            aond.setApproveOrderId(aod.getId());
        });
        approveOrderNodeService.checkApproveOrderNode(aonds);
        approveOrderNodeService.saveBatch(aonds);
        // 保存审批操作
        approveOrderOperService.saveApproveOrderOper(ApproveOrderOperType.submit, aod, null, null, null, null, businessType, aod.getBusinessId());
        this.pushApproveOrder(aod);
    }

    @Transactional
    @Override
    public void revocationApproveOrder(ApproveOrderBusinessType businessType, Long businessId) {
        // 结束审批单，删除审批单节点
        ApproveOrderDto c = new ApproveOrderDto();
        c.setBusinessType(businessType.getKey());
        c.setBusinessId(businessId);
        List<ApproveOrderDto> aods = this.list(c);
        if (CollUtil.isEmpty(aods)) {
            return;
        }
        CollUtil.forEach(aods, (CollUtil.Consumer<ApproveOrderDto>) (aod, index) -> {
            aod.setState(ApproveOrderState.close.getKey());
            this.updateById(aod);
        });
        List<Long> aodIds = aods.stream().map(BaseDto::getId).collect(Collectors.toList());
        List<ApproveOrderNodeDto> aonds = approveOrderNodeService.listByField("approveOrderId", aodIds);
        approveOrderNodeService.removeBatchByList(aonds);
        // 删除审批待办待阅
        approveOrderTobeService.removeApproveOrderTobe(businessType, businessId);
        // 保存审批操作
        approveOrderOperService.saveApproveOrderOper(ApproveOrderOperType.revocation, aods.get(0), null, null, null, null, businessType, businessId);
    }

}
